/*
 * msgvolt.c -  communicate with voltpic module, parse info 
 * 	and send temperature information to msgsrv
 * 
 * Copyright 2014 Kamil Cukrowski <kamil@dyzio.pl>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */
#define MSGVOLT_VERSION "0.1.0"

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <msgserv.h>
#include <libusb-1.0>

#define MSGPRI MSG_NOTICE+4 /* mesg priority */
#define USB_VOLTPIC_VENDOR_ID	0xffff
#define USB_VOLTPIC_PRODUCT_ID	0x0002

#define MY_NAME "msgvolt"
#define PINFO(smt, ...) do { printf(smt, ## __VA_ARGS__ ); printf("\n"); } while(0)
#define PDEBUG(str, ...) PDEBUGL(1, str, ##__VA_ARGS__);
#define PDEBUGL(x, str, ...) do{ if ( debug >= x ) _INTERNAL_LOGIT(str, ##__VA_ARGS__); }while(0)
#define PERROR(str, ...) do { printf(MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("error %d:%s \n", errno, strerror(errno)); }while(0)
#define _INTERNAL_LOGIT(str, ...) do { printf( MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("\n"); }while(0)


int debug = 0;
int volt_fd = 0;
int sockfd;
unsigned char refresh_time = 1;
in_addr_t server_ip = { 2130706433 }; /* 127.0.0.1 */

/* ------------------------------------------------------------------------------- */
/* libusb */
libusb_context *ctx;
struct libusb_device_handle *handle;

int usb_transmit(int endpoint, unsigned char *buff, int *size)
{
	int len;
	int ret;
	
	if ( !handle ) return -1;
	if ( (endpoint&0x01) == 0x01 && size == 0 ) return -1;
	if ( !buff ) return -1;
	
	ret = libusb_bulk_transfer(handle, endpoint, buff, *size, &len, 1000);
	switch(ret) {
	case 0:
		//if ( (endpoint&0x01) == 0x01 ) printf(" sended %d bytes \n", len);
		//else printf(" receied %d bytes \n", len);
		break;
	case LIBUSB_ERROR_TIMEOUT:
		printf(" LIBUSB_ERROR_TIMEOUT \n");
		break;
	case LIBUSB_ERROR_PIPE:
		printf(" LIBUSB_ERROR_PIPE \n");
		break;
	case LIBUSB_ERROR_NO_DEVICE:
		printf(" LIBUSB_ERROR_NO_DEVICE  \n");
		break;
	case LIBUSB_ERROR_OVERFLOW:
		printf("LIBUSB_ERROR_OVERFLOW \n");
		break;
	default:
		printf(" some funckgin error \n");
	}
	
	*size = len;
	return ret;
}

int usb_transmit(int endpoint, unsigned char *buff, int size)
{
	int _size = size;
	return usb_transmit(endpoint, buff, &_size);
}

/* numbers of in and out enpoints */
#define usb_send(buff, size) usb_transmit(0x01, buff, size)
#define usb_receive(buff, size) usb_transmit(0x02, buff, size)

struct libusb_device_handle *_usb_connect(libusb_context *ctx, int vendor, int product)
{
	struct libusb_device_handle *handle;
	int result;
	int restore_kernel_driver = 0;

	result = libusb_init(&ctx);
	if (result < 0) {
		printf("Error %i initializing usb context\n", result);
		goto quit;
	}

	libusb_set_debug(ctx, 3);

	/* Detect device */
	handle = libusb_open_device_with_vid_pid(ctx, vendor, product);
	if (handle == NULL) {
		printf("Cannot find device %hX, %hX\n", vendor, product);
		goto clean_context;
	}

	result = libusb_kernel_driver_active(handle, 0);
	printf(" libusb_kernel_driver_active %d \n", result);
	if (result < 0) {
		printf("Error %i checking kernel driver\n", result);
		goto clean_handle;
	}
	if (result > 1) {
		result = libusb_detach_kernel_driver(handle, 0);
		if (result < 0) {
			printf("Error %i detaching kernel driver\n", result);
			goto clean_handle;
		}
		restore_kernel_driver = 1;
	}

	result = libusb_claim_interface(handle, 0);
	if (result < 0) {
		printf("Error %i claiming interface\n", result);
		goto restore_kernel_driver;
	}

	return handle;
	
//release_interface:
	libusb_release_interface(handle, 0);

 restore_kernel_driver:
	if (restore_kernel_driver)
		libusb_attach_kernel_driver(handle, 0);

 clean_handle:
	libusb_close(handle);

 clean_context:
	libusb_exit(ctx);
	
 quit:
	return NULL;
}

struct libusb_device_handle *_usb_disconnect(libusb_context *ctx, struct libusb_device_handle *handle)
{
	libusb_release_interface(handle, 0);
	libusb_close(handle);
	libusb_exit(ctx);
	return NULL;
}

int usb_connect(int vendor, int product)
{
	handle = usb_connect(ctx, vendor, product);
	if ( handle == NULL )
		return -1;
	return 0;
}

void usb_disconnect()
{
	handle = usb_disconnect(ctx, handle);
}

/* ------------------------------------------------------------------------------- */

void safe_exit()
{
	msglcd_disconnect(sockfd);
	close(volt_fd);
        exit(0);
}

#define fatal(format, ...) \
do {\
        PERROR(format, ##__VA_ARGS__);\
        safe_exit();\
} while (0)

void sig_hup (int param) {
	PDEBUG("Signal received. Going down");
	safe_exit();
}

void parse_cmd(int argc, char *argv[])
{
	int c=0;
	opterr=0;
	while ( (c = getopt(argc, argv, "hVdr:i:")) != -1 )
	switch (c) {
	case 'h':
		printf("msgvolt: display temperature info getter from voltpic driver\n"
		"\t-h\tdisplay this help.\n"
		"\t-d\tdebug mode on (do not fork into the background);\n"
		"\t-r\tset refresh time in secends.\n"
		"\t-i<IP address>\t server ip address.\n"
		"\t-V\tDisplay Version information.\n");
		exit(0);
	case 'd':
		debug=1;
		break;
	case 'r':
		refresh_time=atoi(optarg);
		break;
	case 'i':
		server_ip=inet_addr(optarg);
		break;
	case 'V':
		printf("msgvolt: Version "MSGVOLT_VERSION" \n");
		exit(0);
	}
}

int main(int argc, char *argv[])
{
	char buff[1][41] = { [0] = "                                        " };
	float meter[10] = { [0 ... sizeof(meter)/sizeof(*meter)-1] = 20.0f };
	int v[13];
	int i;

	PDEBUGL(3, "startup ");

	
	signal(SIGHUP,sig_hup);
	signal(SIGQUIT,sig_hup);
	signal(SIGTERM,sig_hup);
	signal(SIGINT,sig_hup);

	parse_cmd(argc,argv);

	if ( !server_ip ) {
		printf(" no server_up given \n");
		safe_exit();
	}
	
	sockfd = msglcd_connect(server_ip);
	if (sockfd < 0)
		fatal("Could not connect to lcd server %d \n", sockfd);
	
	if ( usb_connect(USB_VOLTPIC_VENDOR_ID, USB_VOLTPIC_PRODUCT_ID) < 0 )
		fatal("usb_connect");
	
	
	PDEBUGL(3, "in petla");
	for (;;) {
		
		msgreceive(msg
		/* read device info (array of 10 int in miliVolts ) */
		if ( i < 0) {
			switch (errno) {
			case EINVAL: /*   invalid argument */
				fatal("read fatal");
			default:
				file_open();
			}
		}
		
			for (i=0; i<10; i++) {
				printf("%04x|", v[i]);
			}
			printf("\n");
		for (i=0; i<10; i++) {
			/* output from pic18f2550 */
			meter[i] = ( (float)v[i] * 100.f * ( 1.f - 0.f ) / 1023.f );
			
			sprintf(buff[0]+4*i, "%3.0fC", meter[i]);
		}
		
		if (debug) {
			for (i=0; i<10; i++) {
				printf(" %4.2f |", meter[i]);
			}
			printf("\n");
			for (i=0; i<10; i++) {
				printf("%04x|", (int)meter[i]);
			}
			printf("\n");
			for(i=0; i<40;i++)
				printf("%c", buff[0][i]);
			printf("\n");
			
			fflush(stdout);
		}
		
		if ( msglcd_updat(sockfd, 0, MSGPRI, buff[0], 40) )
			fatal("update mesg ");
		
		sleep(refresh_time);

	}

	close(volt_fd);
	return 0;
}
